home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Converters / Convert_MacPaint / Source / shared.subproj / RCS / ConvertController.m,v < prev    next >
Text File  |  1995-06-12  |  68KB  |  1,998 lines

  1. head     1.4;
  2. branch   ;
  3. access   ;
  4. symbols  beta10:1.3;
  5. locks    death:1.4;
  6. comment  @@;
  7.  
  8.  
  9. 1.4
  10. date     93.04.04.23.44.18;  author death;  state Exp;
  11. branches ;
  12. next     1.3;
  13.  
  14. 1.3
  15. date     93.01.10.15.07.50;  author death;  state Exp;
  16. branches ;
  17. next     1.2;
  18.  
  19. 1.2
  20. date     92.07.26.13.57.36;  author death;  state Exp;
  21. branches ;
  22. next     1.1;
  23.  
  24. 1.1
  25. date     92.07.26.13.51.42;  author death;  state Exp;
  26. branches ;
  27. next     ;
  28.  
  29.  
  30. desc
  31. @Basic and primitive convert controller object
  32. @
  33.  
  34.  
  35. 1.4
  36. log
  37. @Sun Apr  4 23:44:18 PDT 1993
  38. @
  39. text
  40. @#import "ConvertController.h"
  41. #import "File.h"
  42. #import  "AbstractConverter.h"        // mainly to stop annoying 'I don't know about this runtime method call'
  43.  
  44. #import <stdio.h>
  45. #import <stdlib.h>
  46. #import <string.h>
  47. #import <appkit/Panel.h> 
  48. #import <appkit/Window.h>        // for manipulations of ProgressWindow
  49. #import <appkit/Matrix.h>
  50. #import <appkit/Cell.h>
  51. #import <appkit/SavePanel.h>
  52. #import <appkit/OpenPanel.h>
  53. #import <libc.h>                //for getwd
  54. #import <sys/param.h>
  55. //
  56. //    Include the following 2 for drag and drop facility, plus for IAC communications.
  57. //
  58. #import "ConvertListener.h"
  59. #import "ConvertSpeaker.h"
  60. #import <appkit/Application.h>        // To get NXApp (I thought that is what defaults was...)
  61. #import <appkit/publicWraps.h>    // for NXConvertWinNumToGlobal
  62. #import <appkit/graphics.h>        // For NXPing
  63. #import <appkit/publicWraps.h>     //    For NXBeep
  64.  
  65. @@implementation ConvertController
  66.  
  67. /*============================================================*\
  68.  
  69.     The following routines are all for the application delegate responsibilities of this
  70.     class.  Basically dealing with starting up, shutting down, and with allowing the
  71.     user to double click on a file in the workspace and have it open up, and setting up
  72.     for providnig communications between applications
  73.  
  74. \*============================================================*/
  75.  
  76. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  77. //    Routine:        appWillInit:
  78. //    Parameters:    our caller
  79. //    Returns:        self
  80. //    Stores:        none
  81. //    Description:
  82. //        This is used to store a specialized speaker and listener in the application.
  83. //        these specialized objects are subclasses of the normal speaker and listener
  84. //        classes, and together implement a method to allow the caller to instruct this
  85. //        class to convert a file.
  86. //    Bugs:
  87. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  88. - appWillInit: sender
  89. {
  90.     Instance        mySpeaker;
  91.     Instance        myListener;
  92.     //
  93.     //    Set up the conversion-aware speaker
  94.     //
  95.     mySpeaker = [[ConvertSpeaker alloc] init];
  96.     [mySpeaker setDelegate:NXApp];
  97.     [NXApp setAppSpeaker:mySpeaker];
  98.     //
  99.     //    Set up the conversion-aware listener
  100.     //
  101.     myListener = [[ConvertListener alloc] init];
  102.     [myListener setDelegate:NXApp];
  103.     [NXApp setAppListener:myListener];
  104.     
  105.     return self;
  106. }
  107.  
  108.  
  109. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  110. //    Routine:        appDidInit:
  111. //    Parameters:    our caller
  112. //    Returns:        self
  113. //    Stores:        none
  114. //    Description:
  115. //        This is for when this class acts as a delegate to the application.  It allows it to
  116. //        do some final initialization stuff.  The big thins is to allow us to register out
  117. //        window so the user will be able to drag icons over it, and thus serve as the
  118. //        drag-and-drop facilities that we support.  This later causes iconEntered: and
  119. //        iconReleasedAt::ok: messages to be sent later.
  120. //        The 2.x code for the drag-and-drop was mainly copied from DrawDocument.
  121. //        The 3.x code is copied straight from NeXT's Window.rtf help doc.   The window
  122. //        registration for 3.0 is much more straight forward.  
  123. //    Bugs:
  124. //        Perhaps I should be using appDidBecomeActive here some?
  125. //    History
  126. //        93.01.24    djb    Modified so will make use of NS 3.0 drag and drop
  127. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  128. - appDidInit: sender
  129. {
  130. #if ((NSmajor == 2) || (DoNS3DragNDrop == 0))
  131.     //
  132.     //    Allocate a listener instance, and store as one of our instance variables.
  133.     //    Set it up as using our private port, and have the app's speaker register
  134.     //    the window and port.
  135.     //
  136.     listener = [[Listener allocFromZone:[self zone]] init]; // from MY zone?
  137.     [listener setDelegate:self];    // We'll now get the messages from the workspace
  138.     [listener usePrivatePort];
  139.     [listener addPort];
  140. #endif
  141.     [self   allowDragAndDrop];    // do nothing special for NS 3.0 style
  142.     //
  143.     //    Force the window the front, and give it's miniwindow an icon.
  144.     //
  145.     [ProgressWindow setMiniwindowIcon: "Application"];
  146.     [ProgressWindow makeKeyAndOrderFront:self];
  147.     //
  148.     //    93.01.08    djb    Whoops.  Wasn't disabling parts of edit menu right off as needs to be
  149.     //
  150.     [cutCommand        setEnabled: NO];
  151.     [pasteCommand        setEnabled: NO];
  152.     [spellingCommand    setEnabled: NO];
  153.     [checkSpellingCommand    setEnabled: NO];
  154.     
  155.     return self;
  156. }
  157.  
  158.  
  159. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  160. //    Routine:        appWillTerminate:
  161. //    Parameters:    our caller
  162. //    Returns:        self
  163. //    Stores:        none
  164. //    Description:
  165. //        This serves tobalance the init above!
  166. //        Mainly, it unregisters our window information that was used to allow the
  167. //            workspace to have the user drag folders over our window!
  168. //        This code is basically copied directly from DrawDocument.m  =)
  169. //    Bugs:
  170. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  171. - appWillTerminate: sender
  172. {
  173.     [self   refuseDragAndDrop];
  174.     if (listener != NullInstance)
  175.         [listener   free];
  176.     return self;
  177. }
  178.  
  179.  
  180. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  181. //    Routine:        appAcceptsAnotherFile:
  182. //    Parameters:    our caller
  183. //    Returns:        self
  184. //    Stores:        none
  185. //    Description:
  186. //        If we can accept another file to be opened, we return YES.  If we can't we respond
  187. //        NO.  For now, we always return that we'll accept another file.  This just ends
  188. //        up queueing files waiting to be converted.
  189. //    Bugs:
  190. //        To be honest, this makes me a bit uncomfortable.  I feel as though I should be
  191. //        keeping some kinda lock and returning NO if I'm presently converting
  192. //        something.  yet, if I'm converting something, nothing else should be able to
  193. //        be doing any execution here, so I guess it doesn't matter.  
  194. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  195.  
  196. - (BOOL)appAcceptsAnotherFile:sender
  197. {
  198.         return YES;
  199. }
  200.  
  201.  
  202. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  203. //    Routine:        app:openFile:type:
  204. //    Parameters:    The name of the file to be converted
  205. //                It's extension
  206. //    Returns:        a flag indicating whether we can deal with the file
  207. //    Stores:        none
  208. //    Description:
  209. //        The user double clicked on a file in the workspace, and now we are asked to open it.
  210. //        All this does, though, is pass the file information on to the first level conversion
  211. //        method and let it try to deal with it.
  212. //    Bugs:
  213. //        Might it be nicer to play a game, like below, where we do a 'perform' to do the
  214. //        conversion after we return YES to the caller?  That begins to seem a bit
  215. //        dangerous to me.  still.....
  216. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  217.  
  218. - (int)app:sender openFile:(const char *)filename type:(const char *)aType
  219. {
  220.     CString    tempString;
  221.     //
  222.     //    Ask the convert controlling object to convert this particular file
  223.     //
  224.     [self   ConvertThisFile: filename];
  225.     //
  226.     //    Check if the file was converted alright...
  227.     //
  228.     if ([self   GetErrorCode] == ERR_OK)
  229.     {
  230.         [SourceTitle    setStringValue: "Converted:"];
  231.         [Status    setStringValue: "The conversion is done."];
  232.         [self StoreErrorCode:  ERR_OK AndText: "Conversion process done"];
  233.     }
  234.     else
  235.     {
  236.         [SourceTitle    setStringValue: "Failed:"];
  237.         tempString = [self   GetErrorText];
  238.         [Status    setStringValue: tempString];
  239.         FreeCString(tempString);
  240.     }
  241.  
  242.     return YES;    // Always report that it was converted ok
  243. }
  244.  
  245.  
  246. /*============================================================*\
  247.  
  248.     The following routines are all for the drag-and-drop facilities which allow the
  249.     user to drag files from the workspace, and drop them over the main window of
  250.     the application.  Fun with InterProcessCommuniction. =)
  251.  
  252. \*============================================================*/
  253.  
  254.  
  255. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  256. //    Routine:        iconEntered:at:iconWindow:iconX:iconY:iconWidth:iconHeight:pathList:
  257. //    Parameters:    various, mostly not important at the moment
  258. //    Returns:        0 if successfull, something else if not
  259. //    Stores:        none
  260. //    Description:
  261. //        When the user drags a clump of one of more files over our window, we get
  262. //        this message.  We store away the list of files, because we are not given it
  263. //        later on.  =(  Note that we first free any pending set of file paths in the
  264. //        extremely unlikely condition that there is one already there, and then
  265. //        copy the list of paths for use in iconReleasedAt: below
  266. //    Bugs:
  267. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  268. - (int)iconEntered:(int)windowNum at:(double)x :(double)y
  269.     iconWindow:(int)iconWindowNum iconX:(double)iconX iconY:(double)iconY
  270.     iconWidth:(double)iconWidth iconHeight:(double)iconHeight
  271.     pathList:(char *)pathList
  272. {
  273.     if (filePaths != NullCString)
  274.         FreeCString(filePaths);
  275.     filePaths = NewCString(strlen(pathList));
  276.     strcpy(filePaths, pathList);
  277.     return 0;
  278. }
  279.  
  280.  
  281. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  282. //    Routine:        iconMovedTo::
  283. //    Parameters:    n/a
  284. //    Returns:        n/a
  285. //    Stores:        n/a
  286. //    Description:
  287. //        I'm NOT implimenting this, though it might still be nice if we wanted some
  288. //        kinda graphical effect, or if we could enforce dropping in only a particular
  289. //        area (I like this idea, rather than the generic droppoing we have now...
  290. //        (heh.  drop on the NeXT icon to convert it TO NeXt, drop it on the Mac
  291. //        to convert TO mac.  =)  However, the below code demonstrates that one can
  292. //        not change the little-copy-cursor-icon-thingie based on a return value
  293. //    Bugs:
  294. //        None, of course, since it is commented out, unless you count that I've grotesquely put it
  295. //        all on one line so one doesn't mistake that it is in fact commented out for now.
  296. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  297. /*- (int)iconMovedTo:(double)x :(double)y { if ((x > 100) || (y > 100)) {NXBeep(); return 0;} else  return 1; } */
  298.  
  299.  
  300.  
  301. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  302. //    Routine:        iconReleasedAt::ok:
  303. //    Parameters:    x and y coords of the dropping point
  304. //    Returns:        0 if successfull, something else if not
  305. //                a ptr to a boolean flag returned by reference
  306. //    Stores:        none
  307. //    Description:
  308. //        When the user droppes the files, we inform the workspace manager that we
  309. //        can deal with them (always).  We then ask ourselves to do something in
  310. //        some fraction of a second.
  311. //        The reason for all this is simple: processing the files is apt to take some time,
  312. //        and we don't want that icon hanging on the window while we do so.  So,
  313. //        we gotta return soon.  Fact is, we also want to be liberal about what kind
  314. //        of files to accept, since in general many of these conversion programs may not
  315. //        have files with 'proper' extensions (e.g. what isthe extension for a Mac font?).
  316. //        So, we tell the workspace all is well, so the icon goes away.  Soon, we get
  317. //        reminded to ProcessDroppedFiles, which will go through and actually start
  318. //        the processing.
  319. //    Bugs:
  320. //        As with some of the routines above, a bit of me is concerned that if this code is
  321. //        executed while this instance is also doing some conversion, trouble will result.
  322. //        but, then, of course, this isn't a multi-threaded thing, so I shouldn't need to
  323. //        worry about that.  Why does my mind keep trying to think in terms of a
  324. //        processing architecture that doesn't exist yet?
  325. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  326. - (int)iconReleasedAt:(double)x :(double)y ok:(int *)flag
  327. {
  328.  
  329.     [self   perform: @@selector(ProcessDroppedFiles)
  330.         with: self
  331.         afterDelay: 100
  332.         cancelPrevious: NO];
  333.     *flag = YES;
  334.     return 0;
  335. }
  336.  
  337.  
  338. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  339. //    Routine:        iconExitedAt::
  340. //    Parameters:    The x and y coordinates at which the icon left the window
  341. //    Returns:        0 if successfull, something else if not
  342. //    Stores:        none
  343. //    Description:
  344. //        If the user drags the icon off the window, we just free the string of
  345. //        paths and reutrn.
  346. //    Bugs:
  347. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  348. - (int)iconExitedAt:(double)x :(double)y
  349. {
  350.     if (filePaths != NullCString)
  351.     {
  352.         FreeCString(filePaths);
  353.         filePaths = NullCString;
  354.     }
  355.     return 0;
  356. }
  357.  
  358.  
  359. #if (NSmajor == 3)
  360.  
  361. /*============================================================*\
  362.  
  363.     The following routines are NeXTSTEP 3.0 routines that will allow for drag and drop
  364.     of filenames.  NOTE that these are equivalent to the routines above, and that both
  365.     are being maintained so I can recompile easily under 2.1 if needed.
  366.     NOTE!  For this to work, the instance that this is must be designated the delagate
  367.     of the progresswindow!
  368.     It Might be more efficient, by the way, instead of registering and undregistering the
  369.     accepted types all the time, to instead set a flag and return NX_DragOperationNone.
  370.  
  371. \*============================================================*/
  372.  
  373. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  374. //    Routine:        draggingEntered:
  375. //    Parameters:    the caller
  376. //    Returns:        NX_DragOperationCopy
  377. //    Stores:        none
  378. //    Description:
  379. //        Aside from telling the caller that we'll happily convert whatever files we're
  380. //        being asked about, this also gets and stores the tab delimited list of file names
  381. //        for potential later processing.
  382. //    Bugs:
  383. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  384. - (NXDragOperation)draggingEntered:(id)sender
  385. {
  386.     Instance    filenamePasteboard;
  387.     CString    names;
  388.     int        thelength;
  389.     filenamePasteboard = [sender  draggingPasteboard];
  390.  
  391.     if (filePaths != NullCString)
  392.         FreeCString(filePaths);
  393.     [filenamePasteboard   readType: NXFilenamePboardType data: &names
  394.             length: &thelength];
  395.     filePaths = NewCString(thelength);
  396.     strcpy(filePaths, names);
  397.     [filenamePasteboard   deallocatePasteboardData: names length: thelength];
  398.  
  399.     return NX_DragOperationCopy;
  400. }
  401.  
  402.  
  403. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  404. //    Routine:        draggingUpdated:
  405. //    Parameters:    the caller
  406. //    Returns:        NX_DragOperationCopy
  407. //    Stores:        none
  408. //    Description:
  409. //        This merely indicates that we'll be happy to try to convert the specified
  410. //        filenames.  We aren't particular aboout what kinds of files they are. =)
  411. //    Bugs:
  412. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  413. - (NXDragOperation)draggingUpdated:(id)sender
  414. {
  415.     return NX_DragOperationCopy;
  416. }
  417.  
  418. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  419. //    Routine:        prepareForDragOperation:
  420. //    Parameters:    the caller
  421. //    Returns:        YES
  422. //    Stores:        none
  423. //    Description:
  424. //        We will always do the drag operation.
  425. //    Bugs:
  426. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  427. - (BOOL)prepareForDragOperation:(id)sender
  428. {
  429.     return YES;
  430. }
  431.  
  432.  
  433. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  434. //    Routine:        performDragOperation:
  435. //    Parameters:    the caller
  436. //    Returns:        YES
  437. //    Stores:        none
  438. //    Description:
  439. //        Convert the file(s).  Indicate that we could.
  440. //    Bugs:
  441. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  442.  - (BOOL)performDragOperation:(id)sender
  443. {
  444.     [self   perform: @@selector(ProcessDroppedFiles)
  445.         with: self
  446.         afterDelay: 100
  447.         cancelPrevious: NO];
  448.     return YES;
  449. }
  450.  
  451. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  452. //    Routine:        concludeDragOperation:
  453. //    Parameters:    the caller
  454. //    Returns:        self
  455. //    Stores:        none
  456. //    Description:
  457. //        Do nothing, as there's nothing to clean up, really (we could free the copy of
  458. //        the pathnames, but there's no real urgent need).
  459. //    Bugs:
  460. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  461. - concludeDragOperation:(id)sender
  462. {
  463.     return self;
  464. }
  465.  
  466. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  467. //    Routine:        draggingExited:
  468. //    Parameters:    the caller
  469. //    Returns:        returns self
  470. //    Stores:        none
  471. //    Description:
  472. //        If the user drags the icon off the window, we just free the string of
  473. //        paths and reuturn
  474. //    Bugs:
  475. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  476. - draggingExited:(id)sender
  477. {
  478.     if (filePaths != NullCString)
  479.     {
  480.         FreeCString(filePaths);
  481.         filePaths = NullCString;
  482.     }
  483.     return self;
  484. }
  485.  
  486. #endif
  487.  
  488.  
  489. /*============================================================*\
  490.  
  491.     The following routines are various basic utility routines used by  the above
  492.     methods.  In short:  toggle drag-and-drop on and off,  and process the file names
  493.     when/if they are actually dropped.
  494.  
  495. \*============================================================*/
  496.  
  497.  
  498. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  499. //    Method:        refuseDragAndDrop
  500. //    Parameters:    none
  501. //    Returns:        self
  502. //    Stores:        none
  503. //    Description:
  504. //        This handy method is used to turn off our request to allow files to be dropped
  505. //        over the application's window.  Clearly, it has conditionally compiled sections
  506. //        for NS 3.0 and 2.x style drag and drop
  507. //    Bugs:
  508. //    History:
  509. //        93.01.24    djb    Added support for 3.0 style drag and drop
  510. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  511.  
  512. - refuseDragAndDrop
  513. {
  514. #if ((NSmajor == 2) || (DoNS3DragNDrop == 0))
  515.     unsigned int        windowNum;
  516.     Instance            speaker = [NXApp appSpeaker];
  517.  
  518.     if (listener != NullInstance)
  519.     {
  520.         [speaker   setSendPort: NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
  521.         NXConvertWinNumToGlobal([ProgressWindow   windowNum], &windowNum);
  522.         [speaker   unregisterWindow: windowNum];
  523.     }
  524. #else
  525.     [ProgressWindow   unregisterDraggedTypes];
  526. #endif
  527.  
  528.     return self;
  529. }
  530.  
  531.  
  532. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  533. //    Method:        allowDragAndDrop
  534. //    Parameters:    none
  535. //    Returns:        self
  536. //    Stores:        none
  537. //    Description:
  538. //        This turns on the ability to have files droped over our window so we can
  539. //        process them.   It has code fro both NS 2.x and 3.x style implementations
  540. //    Bugs:
  541. //    History:
  542. //        93.01.24    djb    Added support for 3.0 style drag and drop
  543. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  544.  
  545. - allowDragAndDrop
  546. {
  547. #if ((NSmajor == 2) || (DoNS3DragNDrop == 0))
  548.     unsigned int        windowNum;
  549.     Instance            speaker = [NXApp appSpeaker];
  550.  
  551.     NXConvertWinNumToGlobal([ProgressWindow   windowNum], &windowNum);
  552.     [speaker   setSendPort: NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
  553.     [speaker   registerWindow:windowNum toPort:[listener   listenPort]];
  554. #else
  555.     //
  556.     //    We must tell the window to be ready for some dragged file names
  557.     //
  558.     const char *dataType[] = {NXFilenamePboardType};
  559.     [ProgressWindow   registerForDraggedTypes: dataType   count:1];
  560. #endif
  561.  
  562.     return self;
  563. }
  564.  
  565.  
  566.  
  567. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  568. //    Routine:        ProcessDroppedFiles
  569. //    Parameters:    none
  570. //    Returns:        self
  571. //    Stores:        none
  572. //    Description:
  573. //        There is, presumably,  string of file names waiting to be processed.  We
  574. //        should process them one at a time, then.  We make a copy of the file name
  575. //        list, and then repeatedly  locate the and start and end of each file path name
  576. //        in the list (they're tab delimited).  For each that we find, we process it.
  577. //    Bugs:
  578. //        We could almost certainly munge the file path string directly, but I feel
  579. //        safer treating it 'right'.
  580. //        If any of the files have tabs in their names, I have no idea what will or will
  581. //        have happened.
  582. //        I chose not to nuke the filePaths at when done here.  This might not be good?
  583. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  584. - ProcessDroppedFiles
  585. {
  586.     CString    allpaths,
  587.             nextpath,
  588.             location;
  589.     CString    tempString;
  590.     
  591.     allpaths = NewCString(strlen(filePaths));
  592.     strcpy(allpaths, filePaths);
  593.     nextpath = allpaths;
  594.     do
  595.     {
  596.         //
  597.         //    Find the next tab.  If we found it, put a null there.  If we did not,
  598.         //    then this is the end of allpaths.  We still have the last file name to
  599.         //    process, so we keep going.  In all cases, convert the file, set up for
  600.         //    the next conversion (if any).  Check whether we reached the end, and
  601.         //    if so quit.
  602.         //
  603.         location = strchr(nextpath, '\t');
  604.         if (location != EndOfCString)
  605.             location[0] = EndOfCString;
  606.         [self   ConvertThisFile: nextpath];
  607.         //
  608.         //    Check if the file was converted alright...
  609.         //
  610.         if ([self   GetErrorCode] == ERR_OK)
  611.         {
  612.             [SourceTitle    setStringValue: "Converted:"];
  613.             [Status    setStringValue: "The conversion is done."];
  614.             [self StoreErrorCode:  ERR_OK AndText: "Conversion process done"];
  615.         }
  616.         else
  617.         {
  618.             [SourceTitle    setStringValue: "Failed:"];
  619.             tempString = [self   GetErrorText];
  620.             [Status    setStringValue: tempString];
  621.             FreeCString(tempString);
  622.         }
  623.         nextpath = location+1;
  624.     }
  625.     while (location != EndOfCString);
  626.     FreeCString(allpaths);
  627.     return self;
  628. }
  629.  
  630.  
  631.  
  632. /*============================================================*\
  633.  
  634.     The following methods are intended to be used to allow a limited dialog as the
  635.     'slave' of another app that wants us to convert a file.
  636.  
  637. \*============================================================*/
  638.  
  639.  
  640. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  641. //    Routine:        msgQuit
  642. //    Parameters:    An integer that we fill in YES if we work (we always work =)
  643. //    Returns:        self
  644. //    Stores:        none
  645. //    Description:
  646. //        This will set up a delayed message to the app to have it quit.  We allow PLENTY
  647. //        of time for us to quit and retun to the caller before that method gets invoked.  If
  648. //        we didn't provide enough time (as, for instance the Draw demo app does not), then
  649. //        the caller will hang until time out because we would never return from the call.
  650. //    Bugs:
  651. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  652. - (int)msgQuit:(int *)flag
  653. {
  654.     [NXApp   perform:@@selector(terminate)  with: NXApp  afterDelay: 300
  655.         cancelPrevious: NO];
  656.     *flag = YES;
  657.     return 0;
  658. }
  659.  
  660.  
  661. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  662. //    Routine:        msgConvert:To:
  663. //    Parameters:    The name of the file to convert from, and the name of the file to convert to.
  664. //    Returns:        self
  665. //    Stores:        none
  666. //    Description:
  667. //        This provides a way for an other application to call us, and get into our
  668. //        main conversion routines below.  This takes the two files, opens them, and jumps
  669. //        to the main conversio method.  If either fails to open, we abort the process.
  670. //        However, the caller never learns of this.
  671. //        These file names, basically, should be complete pathnames
  672. //    Bugs:
  673. //        I still don't understand how we we can be sure that these strings haven't been
  674. //        freed by the time we get to them...
  675. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  676. - msgConvert: (char *) sourceFile  To: (char*) destFile
  677. {
  678.     CString    sourceName = NewCString(strlen(sourceFile));
  679.     CString    destName = NewCString(strlen(destFile));
  680.     CString    dirname;
  681.     CString    filename;
  682.     CString    tempString;
  683.     Instance    source;
  684.     Instance    destination;
  685.  
  686.     strcpy(sourceName, sourceFile);
  687.     strcpy(destName, destFile);
  688.     //
  689.     //    Get ourselves out of the way of the caller.
  690.     //    Bug: We still display our window briefly.
  691.     //
  692.     [NXApp    hide: self];
  693.     
  694.     source = [self openSourceFile: sourceName];
  695.     if ([self   GetErrorCode] == ERR_OK)
  696.     {
  697.         [StatusTitle    setStringValue: "Status:"];
  698.         [Status    setStringValue: "Converting"];
  699.         [SourceTitle    setStringValue: "Converting:"];
  700.         [SourcePathTitle    setStringValue: "In:"];
  701.         filename = [source GetFilename];
  702.         dirname = [source GetDirectory];
  703.         [SourceFileName    setStringValue: filename];
  704.         [SourcePath        setStringValue: dirname];
  705.         FreeCString (filename);
  706.         FreeCString (dirname);
  707.  
  708.         destination = [self openDestFile: destName];
  709.         if ([self   GetErrorCode] == ERR_OK)
  710.         {
  711.             [DestTitle    setStringValue: "To:"];
  712.             [DestPathTitle    setStringValue: "In:"];
  713.  
  714.             filename = [destination GetFilename];
  715.             dirname = [destination GetDirectory];
  716.             [DestFileName    setStringValue: filename];
  717.             [DestPath        setStringValue: dirname];
  718.             FreeCString (filename);
  719.             FreeCString (dirname);
  720.             [self   DoConversionFrom: source  To: destination];
  721.             if ([self   GetErrorCode] == ERR_OK)
  722.             {
  723.                 [SourceTitle    setStringValue: "Converted:"];
  724.                 [Status    setStringValue: "The conversion is done."];
  725.             }
  726.             else
  727.             {
  728.                 [SourceTitle    setStringValue: "Failed:"];
  729.                 tempString = [self   GetErrorText];
  730.                 [Status    setStringValue: tempString];
  731.                 FreeCString(tempString);
  732.             }
  733.         }
  734.         else
  735.         {
  736.             [SourceTitle    setStringValue: "Failed:"];
  737.             [Status    setStringValue: "Could not open the destination file."];
  738.             [source   free];
  739.         }
  740.     }
  741.     else
  742.     {
  743.         [SourceTitle    setStringValue: "Failed:"];
  744.         [Status    setStringValue: "Could not open the source file."];
  745.     }
  746.     FreeCString(sourceName);
  747.     FreeCString(destName);
  748.  
  749.     return 0;
  750. }
  751.  
  752.  
  753.  
  754. /*============================================================*\
  755.  
  756.     The rest of the methods here do the stuff that is the focus of the application.
  757.     Specifically, we init, deal with user preference settings, set the indicator on the
  758.     screen to indicate how far done the conversion is, open and close files and check
  759.     that the file to be converted is OK.
  760.  
  761. \*============================================================*/
  762.  
  763.  
  764. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  765. //    Routine:        init
  766. //    Parameters:    none
  767. //    Returns:        self
  768. //    Stores:        none
  769. //    Description:
  770. //        This overrides the defalut init method.  But it does little else.
  771. //        Other initializations done in AppDidInit, above...
  772. //        Note that subclasses should OVERRIDE this 
  773. //    Bugs:
  774. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  775. - init
  776. {
  777.     CString    temppath,
  778.             fullpath,
  779.             tempPos;
  780.             
  781.     [super init];
  782.     destIsDead = NO;
  783.     //
  784.     //    Clear, if necessary, the string of file names
  785.     //
  786.     filePaths = NullCString;
  787.     //
  788.     //    Provide silly defaul strings.
  789.     //
  790.     ConversionString = "Converting a file to another";
  791.     SourcePrompt = "a file:";
  792.     DestPrompt = "another file:";
  793.     DestExtension = ".a";
  794.     DefaultsOwner = "I have no defaults";
  795.     //
  796.     //    Locate the path where the application exists (if the app is a foo.app type appwrapper,
  797.     //    this includes the foo.app directory).
  798.     //    (if NXArgv doesn't have a full path, build one using getwd)
  799.     //
  800.     AppHome = NewCString(MAXPATHLEN);
  801.     strcpy(AppHome, NXArgv[0]);
  802.     if (AppHome[0] != '/')
  803.     {
  804.         //
  805.         //    We musta been started up from the command line, so use our working
  806.         //    directory to constructe the rest of the path.
  807.         //
  808.         temppath = NewCString(MAXPATHLEN);
  809.         getwd(temppath);
  810.         fullpath = NewCString(strlen(temppath) + 1 + strlen(AppHome));
  811.         sprintf(fullpath, "%s/%s", temppath, AppHome);
  812.         FreeCString(AppHome);
  813.         AppHome = fullpath;
  814.         FreeCString(temppath);
  815.     }
  816.     //
  817.     //    Now, remove the application's name from the end of the path.
  818.     //
  819.     tempPos = strrchr(AppHome, '/');
  820.     tempPos[0] = EndOfCString;
  821.     return self;
  822. }
  823.  
  824.  
  825. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  826. //    Routine:        free
  827. //    Parameters:    none
  828. //    Returns:        self
  829. //    Stores:        none
  830. //    Description:
  831. //        This merely cleans up after ourselves. 
  832. //    Bugs:
  833. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  834. - free
  835. {
  836.     FreeCString(AppHome);
  837.     return self;
  838. }
  839.  
  840. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  841. //    Routine:        GetBoolPref
  842. //    Parameters:    The preference that we are to retrieve the value for
  843. //    Returns:        YES or NO, depending on the value of the preference
  844. //    Stores:        None
  845. //    Description:
  846. //        This is used to lookup the value for a preference in the defaults database,
  847. //        and return a Boolean value to the caller.  It's really just provided as a
  848. //        shortcut to the several lines of code needed to retrieve the value, compare
  849. //        it to a string value of YES or NO, and then return the appropriate result.
  850. //    Bugs:
  851. //        If anything goes wrong, the user will get a NO value, and not an error code.
  852. //        Should I be disposing of the string that I get back?
  853. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  854. - (Boolean) GetBoolPref: (ConstCString) thePreference
  855. {
  856.     ConstCString    theValue;
  857.     Boolean    result;
  858.     
  859.     theValue = NXGetDefaultValue(DefaultsOwner, thePreference);
  860.     if (strcmp(theValue, "YES") == 0)
  861.         result = YES;
  862.     else
  863.         result = NO;
  864.     
  865.     return result;
  866. }
  867.  
  868.  
  869. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  870. //    Routine:        SetBoolPref:To:
  871. //    Parameters:    The preference that we are to retrieve the value for
  872. //                The Boolean value to assign it
  873. //    Returns:        self
  874. //    Stores:        None
  875. //    Description:
  876. //        This is used to store a boolean value for a preference in the defaults database.
  877. //    Bugs:
  878. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  879. - SetBoolPref: (ConstCString) thePreference To: (Boolean) value
  880. {
  881.     if (value == YES)
  882.         NXWriteDefault(DefaultsOwner, thePreference, "YES");
  883.     else
  884.         NXWriteDefault(DefaultsOwner, thePreference, "NO");
  885.     
  886.     return self;
  887. }
  888.  
  889.  
  890. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  891. //    Routine:        SetPref:To:
  892. //    Parameters:    The preference that we are to retrieve the value for
  893. //                The string value to assign it
  894. //    Returns:        self
  895. //    Stores:        None
  896. //    Description:
  897. //        This is used to store a string value for a preference in the defaults database.
  898. //    Bugs:
  899. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  900. - SetPref: (ConstCString) thePreference To: (CString) value
  901. {
  902.     NXWriteDefault(DefaultsOwner, thePreference, value);
  903.     return self;
  904. }
  905.  
  906. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  907. //    Routine:        GetPref:
  908. //    Parameters:    The preference that we are to retrieve the value for
  909. //    Returns:        A copy of the preference string.
  910. //    Stores:        None
  911. //    Description:
  912. //        This retrieves a string from the defaults database associated with the name
  913. //        thePreference, makes a copy of it, and returns it.
  914. //    Bugs:
  915. //    History:
  916. //        93.02.15    djb    Added for the rtf converter's new three option preference.
  917. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  918. - (CString) GetPref: (ConstCString) thePreference
  919. {
  920.     ConstCString    theValue;
  921.     CString            result;
  922.     
  923.     theValue = NXGetDefaultValue(DefaultsOwner, thePreference);
  924.  
  925.     result = NewCString(strlen(theValue));
  926.     strcpy(result, theValue);
  927.     
  928.     return result;
  929. }
  930.  
  931.  
  932.  
  933. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  934. //    Routine:        displayPreferences: 
  935. //    Parameters:    the object that called us
  936. //    Returns:        self
  937. //    Stores:        none
  938. //    Description:
  939. //        This method is indended to be overridden.  The subclass would just do whatever
  940. //        it needs to do to display preferences for the user..
  941. //    Bugs:
  942. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  943. -displayPreferences: target
  944. {
  945.     return self;
  946. }
  947.  
  948.  
  949.  
  950. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  951. //    Routine:        SetPercentageDone: 
  952. //    Parameters:    A new percentage to display
  953. //    Returns:        self
  954. //    Stores:        none
  955. //    Description:
  956. //        This allows a calling object to set the percentage of conversion done so far.
  957. //        Note that we only actually ask the percentage  meter to display itself in
  958. //        in increments of 5%, simply because re-drawing after every percent, for
  959. //        example, is EXTREMELY slow.
  960. //    Bugs:
  961. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  962. - SetPercentageDone: (Real) percentage
  963. {
  964.     if (percentage > lastPercent+5)
  965.     {
  966.         [ProgressMeter  SetTo: percentage];
  967.         lastPercent = percentage;
  968.         NXPing();
  969.     }
  970.     return self;
  971. }
  972.  
  973.  
  974.  
  975. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  976. //    Routine:        openSource and Dest files 
  977. //    Parameters:    the full path of the file to be converted.
  978. //    Returns:        the file object
  979. //    Stores:        errors
  980. //    Description:
  981. //        These tiny methods are provided so a subclass can override what kind of
  982. //        file gets opened for the source or the dest, without overriding the whole
  983. //        of the conversion methods.  Note that they should always open a subclass
  984. //        of File, though.
  985. //    Bugs:
  986. //    Modifications
  987. //        92.11.27    djb    Changed CreateAndOpenFor: to ClearAnd....  Any time this method
  988. //                    is called within the framework of this class, the user has already
  989. //                    specified they definitely want to use this file, even if it already
  990. //                    exists, so we want to just clear the file if it exists already.
  991. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  992. - openDestFile: (roCString) theFile
  993. {
  994.     Instance    fileInstance;
  995.     [self   ResetResults];
  996.     
  997.     fileInstance = [[File alloc] initAndUse: theFile];
  998.     if ([fileInstance   GetErrorCode] == ERR_OK)
  999.         [fileInstance   ClearAndOpenFor: FILE_WRITE];
  1000.     if ([fileInstance   GetErrorCode] != ERR_OK)
  1001.     {
  1002.         [self   StoreErrorCode: ERR_CREATEFAILED
  1003.             AndText: "We failed to create the file"];
  1004.         [fileInstance   free];
  1005.         fileInstance = NullInstance;
  1006.     }
  1007.     return fileInstance;
  1008. }
  1009.  
  1010. - openSourceFile: (roCString) theFile
  1011. {
  1012.     Instance    fileInstance;
  1013.     [self   ResetResults];
  1014.     
  1015.     fileInstance = [[File alloc] initAndUse: theFile];
  1016.     if ([fileInstance   GetErrorCode] == ERR_OK)
  1017.         [fileInstance   OpenExistingFor: FILE_READ];
  1018.     if ([fileInstance   GetErrorCode] != ERR_OK)
  1019.     {
  1020.         [self   StoreErrorCode: ERR_OPENFAILED
  1021.             AndText: "We failed to open the file"];
  1022.         [fileInstance   free];
  1023.         fileInstance = NullInstance;
  1024.     }
  1025.     return fileInstance;
  1026. }
  1027.  
  1028.  
  1029.  
  1030. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1031. //    Routine:        closeSource and Dest files 
  1032. //    Parameters:    the file object to be closed
  1033. //    Returns:        self
  1034. //    Stores:        errors
  1035. //    Description:
  1036. //        These Little methods are used to undo the work of the above open methods.
  1037. //        they close the file objects, and free them.  Like the above, they are pretty
  1038. //        simple and generic, and will probably be overridden by many converters.
  1039. //        Note that the dest file closing method has a boolean flag.  If true, we also delete
  1040. //        the file (presumably something went wrong.
  1041. //    Bugs:
  1042. //    History
  1043. //        93.01.01    djb    Added tempString and freeing of it, so as not to leak memory.
  1044. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1045.  
  1046. - closeSourceFile: fileInstance
  1047. {
  1048.     CString    tempString    =  [fileInstance  GetErrorText];
  1049.     [self   ResetResults];
  1050.  
  1051.     [self   StoreErrorCode: [fileInstance  GetErrorCode]
  1052.         AndText: tempString];
  1053.     FreeCString(tempString);
  1054.     [fileInstance   free];
  1055.  
  1056.     return self;
  1057. }
  1058.  
  1059.  
  1060. - closeDestFile: fileInstance  andDelete: (Boolean) deleteit
  1061. {
  1062.     CString    tempString    =  [fileInstance  GetErrorText];
  1063.     [self   ResetResults];
  1064.  
  1065.     if (deleteit == NO)
  1066.         [fileInstance   CloseAndSave];
  1067.     else
  1068.         [fileInstance   CloseAndDelete];
  1069.     [self   StoreErrorCode: [fileInstance  GetErrorCode]
  1070.         AndText:  tempString];
  1071.     FreeCString(tempString);
  1072.     [fileInstance   free];
  1073.  
  1074.     return self;
  1075. }
  1076.  
  1077.  
  1078.  
  1079. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1080. //    Method:        preConversion
  1081. //    Parameters:    none
  1082. //    Returns:        self
  1083. //    Stores:        none
  1084. //    Description:
  1085. //        This method is called just before calling the conversion routine (i.e. after
  1086. //        files have been located and opened alright).  It does any work needed
  1087. //        to finish up preparing for the conversion.  At the moment, this means unregistering
  1088. //        the window, and setting the progress meter up.  One is welcome to subclass
  1089. //        this method, but should probably [super preConversion] before proceeding there.
  1090. //    Bugs:
  1091. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1092.  
  1093. - preConversion
  1094. {
  1095.     [self   refuseDragAndDrop];
  1096.     lastPercent = 0;
  1097.     [ProgressMeter   ActivateWithGoal: 100];
  1098.     return self;
  1099. }
  1100.  
  1101.  
  1102. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1103. //    Method:        postConversion
  1104. //    Parameters:    none
  1105. //    Returns:        self
  1106. //    Stores:        none
  1107. //    Description:
  1108. //        This method is called just after calling the conversion routine has finished.
  1109. //        It does some work needed to clean up aspects of this instance after the conversion.
  1110. //        At the moment, this means re-registering the window for file-icon-dropping,
  1111. //        and making sure the progress meter shows 100% done.  One is welcome to subclass
  1112. //        this method, but should probably [super postConversion] before proceeding there.
  1113. //    Bugs:
  1114. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1115.  
  1116. - postConversion
  1117. {
  1118.     [self   allowDragAndDrop];
  1119.     [ProgressMeter  SetTo: 100];
  1120.     [ProgressMeter   Deactivate];
  1121.     return self;
  1122. }
  1123.  
  1124.  
  1125. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1126. //    Method:        CheckFileOK
  1127. //    Parameters:    none
  1128. //    Returns:        self
  1129. //    Stores:        none
  1130. //    Description:
  1131. //        This asks the converter if this is an ok file to be converted.  If no, then it
  1132. //        gets the reason why, displays it for the user, and goes into a modal loop to
  1133. //        force the user to decide whether to continue or not.  If the user says continue,
  1134. //        we continue.
  1135. //    Bugs:
  1136. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1137. - (Boolean) CheckFileOK: fileInst
  1138. {
  1139.     cancelResult = NO;
  1140.     if ( [converterInst   respondsTo:@@selector(isThisAGoodFile:)] ) 
  1141.     {
  1142.         cancelResult = [converterInst   isThisAGoodFile: fileInst];
  1143.         if (cancelResult != YES)
  1144.         {
  1145.             [decisionText    setStringValue: [converterInst   GetCStringFrom: SECOND_RESULT]];
  1146.             [decisionWindow   center];
  1147.             [decisionWindow   makeKeyAndOrderFront: self];
  1148.             [NXApp   runModalFor: decisionWindow];
  1149.         }
  1150.     }
  1151.     else
  1152.         cancelResult = YES;
  1153.     return cancelResult;
  1154. }
  1155.  
  1156.  
  1157. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1158. //    Method:        UserDecided
  1159. //    Parameters:    the thing that called us
  1160. //    Returns:        self
  1161. //    Stores:        none
  1162. //    Description:
  1163. //        This method gets called when the user clicks on a button in a window during
  1164. //        the modal loop set up in CheckFileOK, above.  It gets the result and stores it in
  1165. //        cancelResult, closes the window and shuts down the modal loop.
  1166. //    Bugs:
  1167. //        I dislike modality in general.
  1168. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1169. - UserDecided: sender
  1170. {
  1171.     if ( [sender   tag] == 0)
  1172.         cancelResult = NO;
  1173.     else
  1174.         cancelResult = YES;
  1175.     [decisionWindow   close];
  1176.     [NXApp   stopModal];
  1177.     return self;
  1178. }
  1179.  
  1180.  
  1181.  
  1182. /*============================================================*\
  1183.  
  1184.     At last, here are the true guts of the application.   The following three methods
  1185.     deal with processing files.  Briefly, PrepareForConversion: starts from scratch,
  1186.     and prompts the user for a file to convert from.  ConvertThisFile: takes a file name
  1187.     and determines what file to put the conversion results into.  ConvertFrom:To:
  1188.     then just converts between the two files (this last and init are things that
  1189.     subclasses MUST override.
  1190.  
  1191. \*============================================================*/
  1192.  
  1193.  
  1194. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1195. //    Method:        PrepareForConversion:
  1196. //    Parameters:    the object that send this message
  1197. //    Returns:        self
  1198. //    Stores:        error code
  1199. //    Description:
  1200. //        This merely creates the converstion necessary object, and sets its
  1201. //        preference for using curly quotes according to the value stored in this
  1202. //        object.  Then, call the method to start getting information from the user to
  1203. //        do the conversion.
  1204. //    Bugs:
  1205. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1206. - PrepareForConversion: sender
  1207. {
  1208.     Instance            theOpenPanel = [OpenPanel new];
  1209.     CString const *    openedFiles;
  1210.     roCString        theDirectory;
  1211.     CString            fullPath, theFile;
  1212.     CString            tempString;
  1213.     Integer            result, pathLength, index;
  1214.  
  1215.     [self  ResetResults];
  1216.     //
  1217.     //    Set up the  display fields
  1218.     //
  1219.     [SourceFileName    setStringValue: ""];
  1220.     [SourcePath    setStringValue: ""];
  1221.     [SourceTitle    setStringValue: "Converting:"];
  1222.     [SourcePathTitle    setStringValue: "In:"];
  1223.  
  1224.     [DestFileName    setStringValue: ""];
  1225.     [DestPath    setStringValue: ""];
  1226.     [DestTitle    setStringValue: "To:"];
  1227.     [DestPathTitle    setStringValue: "In:"];
  1228.     
  1229.     [StatusTitle    setStringValue: "Status:"];
  1230.     [Status    setStringValue: ConversionString];
  1231.     //
  1232.     //    Prepare the open dialog, and get info from the user.
  1233.     //    @@@@ Bug: it doesn't let the user know what kind of conversion
  1234.     //    @@@@ process is now underway in this dialog
  1235.     //
  1236.     [cutCommand    setEnabled: YES];
  1237.     [pasteCommand    setEnabled: YES];
  1238.     [spellingCommand    setEnabled: YES];
  1239.     [checkSpellingCommand    setEnabled: YES];
  1240.     
  1241.     [theOpenPanel allowMultipleFiles: YES];
  1242.     [theOpenPanel setPrompt: SourcePrompt]; // use instance variable for prompt
  1243.     [theOpenPanel setTitle: "Convert"];
  1244.     result = [theOpenPanel runModal];
  1245.  
  1246.     [cutCommand    setEnabled: NO];
  1247.     [pasteCommand    setEnabled: NO];
  1248.     [spellingCommand    setEnabled: NO];
  1249.     [checkSpellingCommand    setEnabled: NO];
  1250.  
  1251.     if (result != 1)
  1252.     {
  1253.         [self StoreErrorCode:  ERR_USERABORTED
  1254.             AndText: "User canceled when opening a source file!"];
  1255.         [Status    setStringValue: "Conversion process canceled at your request!"];
  1256.     }
  1257.     else
  1258.     {
  1259.         //
  1260.         //    get the set of files, as well as the path to them (or it)
  1261.         //
  1262.         openedFiles = [theOpenPanel filenames];
  1263.         theDirectory = [theOpenPanel directory];
  1264.         pathLength = strlen(theDirectory);
  1265.         //
  1266.         //    For each of the one or more files, allocate a space for the full path.
  1267.         //    construct the path using the directory name and file name.
  1268.         //    then, call the method to finish up, and dispose of the path we built.
  1269.         //    Note: re: the if-then-else to fuse the file name and path
  1270.         //    If the directory path is not empty, and it doesn't end in a slash, then
  1271.         //    we build the full path from dir/file.  Otherwise, do the same without a
  1272.         //    slash.  This last is usefull if someone has chosen a file at the root and we
  1273.         //    don't want a double slash (i.e. //myfile). (the check for zero length is really
  1274.         //    just for completeness...)
  1275.         //
  1276.         for (index=0;  openedFiles[index] != NULL; index++)
  1277.         {
  1278.             theFile = openedFiles[index];
  1279.             fullPath = NewCString(strlen(theFile) + 1 + pathLength);
  1280.             if ((pathLength > 0) && (theDirectory[pathLength-1] != '/'))
  1281.                 sprintf(fullPath, "%s/%s", theDirectory, theFile);
  1282.             else
  1283.                 sprintf(fullPath, "%s%s", theDirectory, theFile);
  1284.             //
  1285.             //    With a path in hand, do the next stage in the converstion.
  1286.             //
  1287.             [self ConvertThisFile: fullPath];
  1288.             FreeCString(fullPath);
  1289.         }
  1290.         //
  1291.         //    Check if the file was converted alright...
  1292.         //
  1293.         if ([self   GetErrorCode] == ERR_OK)
  1294.         {
  1295.             [SourceTitle    setStringValue: "Converted:"];
  1296.             [Status    setStringValue: "The conversion is done."];
  1297.             [self StoreErrorCode:  ERR_OK AndText: "Conversion process done"];
  1298.         }
  1299.         else
  1300.         {
  1301.             [SourceTitle    setStringValue: "Failed:"];
  1302.             tempString = [self   GetErrorText];
  1303.             [Status    setStringValue: tempString];
  1304.             FreeCString(tempString);
  1305.         }
  1306.     }
  1307.     return self;
  1308. }
  1309.  
  1310.  
  1311.  
  1312.  
  1313.  
  1314. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1315. //    Routine: ConvertThisFile 
  1316. //    Parameters: the full path of the file to be converted.
  1317. //    Returns:        self
  1318. //    Stores:        error code
  1319. //    Description:
  1320. //        Given a file, you want to convert it!  First, you make sure you can open the file OK,
  1321. //        then, you ask the user for what the destination file should be called.  Having done
  1322. //        this, you then assure that the destination file can be created without problems,
  1323. //        and then you ask the converting routine to convert between the two..
  1324. //    Bugs:
  1325. //        I find this routine a bit too confusing for my tastes...  (long-winded)
  1326. //        We retrieve some strings below, but can't free them.
  1327. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1328. - ConvertThisFile: (roCString) theFile
  1329. {
  1330.     roCString    destname;
  1331.     Instance        sourceFile, destFile;
  1332.     Integer        outcome;
  1333.     PositiveInteger    extenLen, destLen;
  1334.     CString        filename, directory, tempstring, basename;
  1335.     Instance        theSavePanel;
  1336.     Boolean        goodfile;
  1337.     CString        tempString;
  1338.     //
  1339.     [self ResetResults];
  1340.     //
  1341.     //    Set up the  display fields (we don't always go through prepare for conversion, above.
  1342.     //
  1343.     [SourceFileName    setStringValue: ""];
  1344.     [SourcePath    setStringValue: ""];
  1345.     [SourceTitle    setStringValue: "Converting:"];
  1346.     [SourcePathTitle    setStringValue: "In:"];
  1347.  
  1348.     [DestFileName    setStringValue: ""];
  1349.     [DestPath    setStringValue: ""];
  1350.     [DestTitle    setStringValue: "To:"];
  1351.     [DestPathTitle    setStringValue: "In:"];
  1352.     
  1353.     [StatusTitle    setStringValue: "Status:"];
  1354.     [Status    setStringValue: ConversionString];
  1355.     //
  1356.     //    If possible,, open the source file, and update the display. 
  1357.     //    
  1358.     sourceFile = [self openSourceFile: theFile];
  1359.     if ([self   GetErrorCode] != ERR_OK)
  1360.     {
  1361.         [SourceFileName    setStringValue: theFile];
  1362.         [SourcePath    setStringValue: ""];
  1363.         tempString = [self   GetErrorText];
  1364.         [Status    setStringValue: tempString];
  1365.         FreeCString(tempString);
  1366.     }
  1367.     else
  1368.     {
  1369.         goodfile = [self  CheckFileOK: sourceFile];
  1370.         if (goodfile == NO)
  1371.         {
  1372.             filename = [sourceFile GetFilename];
  1373.             directory = [sourceFile GetDirectory];
  1374.             [SourceFileName    setStringValue: filename];
  1375.             [SourcePath        setStringValue: directory];
  1376.             FreeCString (filename);
  1377.             FreeCString (directory);
  1378.             [Status    setStringValue: "The conversion was canceled at your request"];
  1379.             [self   StoreErrorCode: ERR_USERABORTED  AndText: "The conversion was canceled at your request"];
  1380.         }
  1381.         else
  1382.         {
  1383.             ////
  1384.             //    Get descriptions of the file's name, and use these to build a message
  1385.             //    on the screen for the user, and a prompt in the save panel
  1386.             ////
  1387.             filename = [sourceFile GetFilename];
  1388.             directory = [sourceFile GetDirectory];
  1389.             basename = [sourceFile GetBasename];
  1390.             //
  1391.             //    Update the user about current events.
  1392.             //
  1393.             [SourceFileName    setStringValue: filename];
  1394.             [SourcePath    setStringValue: directory];
  1395.             [Status    setStringValue:  "OK"];
  1396.             //
  1397.             //    Then, build a name for the file we want to convert to
  1398.             //
  1399.             [cutCommand    setEnabled: YES];
  1400.             [pasteCommand    setEnabled: YES];
  1401.             [spellingCommand    setEnabled: YES];
  1402.             [checkSpellingCommand    setEnabled: YES];
  1403.  
  1404.             theSavePanel = [SavePanel   new];
  1405.             [theSavePanel setPrompt: DestPrompt];
  1406.             [theSavePanel setTitle: "Convert to"];
  1407.             tempstring = NewCString(strlen(basename) + strlen(DestExtension));
  1408.             sprintf(tempstring, "%s%s", basename, DestExtension);
  1409.             //    93.01.01    djb    Added this to free the leak with basename
  1410.             FreeCString(basename);
  1411.             outcome = [theSavePanel runModalForDirectory: directory file: tempstring];
  1412.             
  1413.             FreeCString (tempstring);
  1414.             FreeCString (filename);
  1415.             FreeCString (directory);
  1416.  
  1417.             [cutCommand    setEnabled: NO];
  1418.             [pasteCommand    setEnabled: NO];
  1419.             [spellingCommand    setEnabled: NO];
  1420.             [checkSpellingCommand    setEnabled: NO];
  1421.  
  1422.             ////
  1423.             //    Now, check the output of the call to runModalForDirectory:file.  If
  1424.             //    the user canceled, tell the user this.  Otherwise, continue processing.
  1425.             ////
  1426.             if (outcome != 1)
  1427.             {
  1428.                 [self StoreErrorCode:  ERR_USERABORTED
  1429.                     AndText: "Conversion process canceled at your request"];
  1430.                 [SourceTitle    setStringValue: "Canceled:"];
  1431.                 [Status    setStringValue:  "Conversion process canceled at your request!"];
  1432.             }
  1433.             else
  1434.             {
  1435.                 ////
  1436.                 //    Open the destination file.  If we can't, display why, otherwise show the
  1437.                 //    user what the destination file name and path is.   Finally, CONVERT
  1438.                 //    the bloody file!
  1439.                 ////
  1440.                 //
  1441.                 //    See if the file ends with the proper extension.  If so, copy it into a
  1442.                 //    temporary string.  Otherwise, tack the extension onto it...
  1443.                 //
  1444.                 destname = [theSavePanel filename];
  1445.                 extenLen = strlen(DestExtension);
  1446.                 destLen = strlen(destname);
  1447.                 if (strcmp(&destname[destLen - extenLen], DestExtension) == 0)
  1448.                 {
  1449.                     tempstring = NewCString(destLen);
  1450.                     strcpy(tempstring, destname);
  1451.                 }
  1452.                 else
  1453.                 {
  1454.                     tempstring = NewCString(destLen+extenLen);
  1455.                     sprintf(tempstring, "%s%s", destname, DestExtension);
  1456.                 }
  1457.                 //
  1458.                 //    Make sure that this ne name doesn't refer to the source file
  1459.                 //
  1460.                 if ([sourceFile      SameFileAs:  tempstring] == YES) 
  1461.                 {
  1462.                     [SourceTitle    setStringValue: "Error:"];
  1463.                     [Status    setStringValue:  "These refer to the same file.  This is not allowed!" ];
  1464.     
  1465.                     [self StoreErrorCode:  ERR_TRIEDTOOVERWRITE
  1466.                         AndText: "Attempt to overwrite source file with destination file! Conversion NOT done."];
  1467.                     [sourceFile    Close];
  1468.                     FreeCString(tempstring);
  1469.                 }
  1470.                 else
  1471.                 {
  1472.                     destFile = [self openDestFile: tempstring];
  1473.                     FreeCString(tempstring);
  1474.                     if ([self    GetErrorCode] != ERR_OK)
  1475.                     {
  1476.                         [SourceTitle    setStringValue: "Failed:"];
  1477.                         tempString = [self   GetErrorText];
  1478.                         [Status    setStringValue: tempString];
  1479.                         FreeCString(tempString);
  1480.                         [sourceFile    Close];
  1481.                     }
  1482.                     else
  1483.                     {
  1484.                         //
  1485.                         //    we opened the dest file.  So, tell the user, and continue.
  1486.                         //
  1487.                         filename = [destFile GetFilename];
  1488.                         directory = [destFile GetDirectory];
  1489.                         [DestFileName    setStringValue: filename];
  1490.                         [DestPath        setStringValue: directory];
  1491.                         FreeCString (filename);
  1492.                         FreeCString (directory);
  1493.                         [self  DoConversionFrom: sourceFile  To: destFile];
  1494.                     }
  1495.                 }
  1496.             }
  1497.         }
  1498.     }
  1499.  
  1500.     return self;
  1501. }
  1502.  
  1503.  
  1504. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1505. //    Routine:        DoConversionFrom:To: 
  1506. //    Parameters:    The file to be converted from, and the file to be converted to
  1507. //    Returns:        self
  1508. //    Stores:        none
  1509. //    Description:
  1510. //        This is called once we have a source and destination file object opened.
  1511. //        It does any preparation needed by the application, then calls the convertFrom:To:
  1512. //        method, and cleans up afterwards.
  1513. //        Note: tempInst is a kludge that was added for things like rtfConverter which
  1514. //        destroys the destination file and creates a new one.  This allows us to deal with
  1515. //        this situation.
  1516. //    Bugs
  1517. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1518. - DoConversionFrom: sourceFile  To: destinationFile
  1519. {
  1520.     Instance        tempInst;
  1521.     Integer        tempError;
  1522.     CString        errorText;
  1523.     //
  1524.     //    Do some preparing of the conversion window...
  1525.     //
  1526.     [self   preConversion];
  1527.     //
  1528.     //    Dim the menus during any conversion process
  1529.     //
  1530.     [quitCommand    setEnabled: NO];
  1531.     [hideCommand    setEnabled: NO];
  1532.     [infoCommands    setEnabled: NO];
  1533.     [editCommands    setEnabled: NO];
  1534.     [servicesCommands setEnabled: NO];
  1535.     [windowsCommands setEnabled: NO];
  1536.  
  1537.     [Status    setStringValue:  "Converting"];
  1538.     tempInst = [self ConvertFrom: sourceFile To: destinationFile];
  1539.     if (destIsDead == YES)
  1540.         destinationFile = tempInst;
  1541.     destIsDead = NO;
  1542.     [quitCommand    setEnabled: YES];
  1543.     [hideCommand    setEnabled: YES];
  1544.     [infoCommands    setEnabled: YES];
  1545.     [editCommands    setEnabled: YES];
  1546.     [servicesCommands setEnabled: YES];
  1547.     [windowsCommands setEnabled: YES];
  1548.     //
  1549.     //    Close the files, now that we are done converting.
  1550.     //
  1551.     tempError = [self GetErrorCode];
  1552.     errorText = [self GetErrorText];
  1553.     if (tempError < ERR_OK)
  1554.         [self   closeDestFile: destinationFile  andDelete: YES];
  1555.     else
  1556.         [self   closeDestFile: destinationFile  andDelete: NO];
  1557.     [self   closeSourceFile: sourceFile];
  1558.  
  1559.     [self   postConversion];
  1560.  
  1561.     [self   StoreErrorCode: tempError AndText: errorText];
  1562.     FreeCString(errorText);
  1563.     return self;
  1564. }
  1565.  
  1566.  
  1567. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1568. //    Routine:        ConvertFrom:To: 
  1569. //    Parameters:    The file to be converted from, and the file to be converted to
  1570. //    Returns:        self
  1571. //    Stores:        none
  1572. //    Description:
  1573. //        This is a method indended to be subclassed.  It would do the actual conversion
  1574. //        of the source to destination file.
  1575. //    Bugs
  1576. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1577. - ConvertFrom: sourceFile To: destinationFile
  1578. {
  1579.     //
  1580.     //    If our converter instance has the ReportTo: method, then we tell it to report to it
  1581.     //
  1582.     if ( [converterInst respondsTo:@@selector(ReportTo:)] ) 
  1583.         [converterInst   ReportTo: self];
  1584.     [Status    setStringValue:  "Converting"];
  1585.     return self;
  1586. }
  1587.  
  1588. @@end
  1589. @
  1590.  
  1591.  
  1592. 1.3
  1593. log
  1594. @Sun Jan 10 15:07:50 PST 1993
  1595. @
  1596. text
  1597. @d81 3
  1598. a83 1
  1599. //        This code for the drag-and-drop was mainly copied from DrawDocument.
  1600. d86 2
  1601. d91 1
  1602. d101 2
  1603. a102 1
  1604.     [self   allowDragAndDrop];
  1605. d111 2
  1606. a112 2
  1607.     [cutCommand    setEnabled: NO];
  1608.     [pasteCommand    setEnabled: NO];
  1609. d320 2
  1610. d324 128
  1611. d466 2
  1612. a467 1
  1613. //        over the application's window.
  1614. d469 2
  1615. d475 1
  1616. d485 4
  1617. d500 1
  1618. a500 1
  1619. //        process them. 
  1620. d502 2
  1621. d508 1
  1622. d515 7
  1623. d866 26
  1624. @
  1625.  
  1626.  
  1627. 1.2
  1628. log
  1629. @Hopefully a pretty final version (it lived through the Font contrller...
  1630. @
  1631. text
  1632. @d14 1
  1633. a14 1
  1634. #import <libc.h>                    //for getwd()
  1635. d16 5
  1636. a20 2
  1637. #import <appkit/Listener.h>        // for the drag-and-drop of files facility
  1638. #import <appkit/Speaker.h>        // ibid
  1639. d23 2
  1640. d32 2
  1641. a33 1
  1642.     user to double click on a file in the workspace and have it open up.
  1643. d37 31
  1644. d69 1
  1645. d102 8
  1646. d175 1
  1647. d180 16
  1648. d197 1
  1649. a197 1
  1650.     return YES;    // Rashly assume that it got converted OK.  =)
  1651. d396 1
  1652. d414 16
  1653. a432 1
  1654.     
  1655. d438 1
  1656. d441 122
  1657. d584 4
  1658. d589 1
  1659. d602 26
  1660. a627 1
  1661.  
  1662. d633 15
  1663. d739 1
  1664. a739 1
  1665. //        in increments of 15%, simply because re-drawing after every percent, for
  1666. d745 1
  1667. a745 1
  1668.     if (percentage > lastPercent+15)
  1669. d749 1
  1670. d767 5
  1671. d780 1
  1672. a780 1
  1673.         [fileInstance   CreateAndOpenFor: FILE_WRITE];
  1674. d823 2
  1675. d829 1
  1676. a831 1
  1677. //    [fileInstance   Close];
  1678. d833 2
  1679. a834 1
  1680.         AndText:  [fileInstance  GetErrorText]];
  1681. d843 1
  1682. d851 2
  1683. a852 1
  1684.         AndText:  [fileInstance  GetErrorText]];
  1685. d993 1
  1686. d1017 5
  1687. d1026 6
  1688. d1083 3
  1689. a1085 1
  1690.             [Status    setStringValue: [self   GetErrorText]];
  1691. d1118 1
  1692. a1118 2
  1693.     Integer        tempError;
  1694.     CString        errorText;
  1695. d1144 3
  1696. a1146 1
  1697.         [Status    setStringValue: [self   GetErrorText]];
  1698. d1153 6
  1699. a1158 3
  1700.             //Bug:  we are loosing these strings (cn't free them)
  1701.             [SourceFileName    setStringValue: [sourceFile GetFilename]];
  1702.             [SourcePath    setStringValue:  [sourceFile GetDirectory]];
  1703. d1180 5
  1704. d1190 2
  1705. a1191 1
  1706.             
  1707. d1197 6
  1708. d1258 3
  1709. a1260 1
  1710.                         [Status    setStringValue: [self     GetErrorText]];
  1711. a1265 4
  1712.                         //    Do some preparing of the conversion window...
  1713.                         //
  1714.                         [self   preConversion];
  1715.                         //
  1716. d1274 1
  1717. a1274 19
  1718.  
  1719.                         [Status    setStringValue:  "Converting"];
  1720.                         [self ConvertFrom: sourceFile To: destFile];
  1721.                         //
  1722.                         //    Close the files, now that we are done converting.
  1723.                         //
  1724.                         tempError = [self GetErrorCode];
  1725.                         errorText = [self GetErrorText];
  1726.                         if (tempError < ERR_OK)
  1727.                             [self   closeDestFile: destFile  andDelete: YES];
  1728.                         else
  1729.                             [self   closeDestFile: destFile  andDelete: NO];
  1730.                         [self   closeSourceFile: sourceFile];
  1731.  
  1732.                         [self   postConversion];
  1733.  
  1734.                         [self   StoreErrorCode: tempError AndText: errorText];
  1735.                         FreeCString(errorText);
  1736.  
  1737. d1286 63
  1738. d1365 1
  1739. @
  1740.  
  1741.  
  1742. 1.1
  1743. log
  1744. @Initial revision
  1745. @
  1746. text
  1747. @d2 2
  1748. d9 1
  1749. d14 1
  1750. a14 3
  1751. #import "File.h"
  1752. #import <libc.h>                //for getwd
  1753. #import <appkit/defaults.h>    // for NXApp to get path to app
  1754. d16 4
  1755. d21 1
  1756. d23 1
  1757. d25 3
  1758. a27 1
  1759. @@implementation ConvertController
  1760. d29 1
  1761. d31 1
  1762. d33 1
  1763. a33 1
  1764. //    Routine:        appDidInit
  1765. d39 5
  1766. a43 1
  1767. //        do some final initialization stuff
  1768. d45 1
  1769. d49 13
  1770. d63 1
  1771. d69 301
  1772. d376 2
  1773. d383 13
  1774. d473 1
  1775. a473 1
  1776. //        This method is indended to be overridden.  THe subclass would just do whatever
  1777. d485 233
  1778. d733 1
  1779. a733 1
  1780.     ConstCString        theDirectory;
  1781. d806 1
  1782. a819 25
  1783. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1784. //    Method:        ConvertOneFile:OfType:
  1785. //    Parameters:    The name of a file, and its type
  1786. //    Returns:        self
  1787. //    Stores:        nothing
  1788. //    Description:
  1789. //        This method is indented mainly to be used for the applicaication delegate to contact
  1790. //        when the user has double clicked on a file in the workspace.  This, in turn, does all
  1791. //        the work necessary to then proceed and call the routine below to get the name of
  1792. //        the file to convert to.   By default this ignores the type.
  1793. //    Bugs:
  1794. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1795. - ConvertOneFile: (CString) filename OfType: (CString) type
  1796. {
  1797.     [self  ResetResults];
  1798.     //
  1799.     //    Clear the display fields, and call the conversion routine
  1800.     //
  1801.     //
  1802.     //    Set up the  display fields
  1803.     //
  1804.     [SourceFileName    setStringValue: ""];
  1805.     [SourcePath    setStringValue: ""];
  1806.     [SourceTitle    setStringValue: "Converting:"];
  1807.     [SourcePathTitle    setStringValue: "In:"];
  1808. a820 7
  1809.     [DestFileName    setStringValue: ""];
  1810.     [DestPath    setStringValue: ""];
  1811.     [DestTitle    setStringValue: "To:"];
  1812.     [DestPathTitle    setStringValue: "In:"];
  1813.     
  1814.     [Status    setStringValue:  ConversionString];
  1815.     [StatusTitle    setStringValue: "Status:"];
  1816. a821 16
  1817.     [self ConvertThisFile: filename];
  1818.     //
  1819.     //    Check if the file was converted alright...
  1820.     //
  1821.     if ([self   GetErrorCode] == ERR_OK)
  1822.     {
  1823.         [Status    setStringValue:  "The conversion is done."];
  1824.         [self StoreErrorCode:  ERR_OK AndText: "Conversion process done"];
  1825.     }
  1826.     else
  1827.     {
  1828.         [SourceTitle    setStringValue: "Failed:"];
  1829.         [Status    setStringValue: [self   GetErrorText]];
  1830.     }
  1831.     return self;
  1832. }
  1833. a822 2
  1834.  
  1835.  
  1836. a823 26
  1837. //    Routine:        openSource and Dest files 
  1838. //    Parameters:    the full path of the file to be converted.
  1839. //    Returns:        the file object
  1840. //    Stores:        none
  1841. //    Description:
  1842. //        These tiny methods are provided so a subclass can override what kind of
  1843. //        file gets opened for the source or the dest, without overriding the whole
  1844. //        of the conversion methods.  Note that they should always open a subclass
  1845. //        of File, though.
  1846. //    Bugs:
  1847. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1848. - openDestFile: (CString) theFile
  1849. {
  1850.     return [[File alloc] initAndUse: theFile];
  1851. }
  1852.  
  1853. - openSourceFile: (CString) theFile
  1854. {
  1855.     return [[File alloc] initAndUse: theFile];
  1856. }
  1857.  
  1858.  
  1859.  
  1860.  
  1861.  
  1862. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1863. d834 2
  1864. a835 1
  1865. //        I find this routine a bit too confusing for my tastes...
  1866. d837 1
  1867. a837 1
  1868. - ConvertThisFile: (CString) theFile
  1869. d839 1
  1870. a839 1
  1871.     ConstCString        destname;
  1872. d845 3
  1873. d851 15
  1874. d869 1
  1875. a869 2
  1876.     [sourceFile OpenExistingFor:  FILE_READ];
  1877.     if ([sourceFile GetErrorCode] != ERR_OK)
  1878. d873 1
  1879. a873 1
  1880.         [Status    setStringValue: [sourceFile   GetErrorText]];
  1881. d877 2
  1882. a878 32
  1883.         ////
  1884.         //    Get descriptions of the file's name, and use these to build a message
  1885.         //    on the screen for the user, and a prompt in the save panel
  1886.         ////
  1887.         filename = [sourceFile GetFilename];
  1888.         directory = [sourceFile GetDirectory];
  1889.         basename = [sourceFile GetBasename];
  1890.         //
  1891.         //    Update the user about current events.
  1892.         //
  1893.         [SourceFileName    setStringValue: filename];
  1894.         [SourcePath    setStringValue: directory];
  1895.         [Status    setStringValue:  "OK"];
  1896.         //
  1897.         //    Then, build a name for the file we want to convert to
  1898.         //
  1899.         theSavePanel = [SavePanel   new];
  1900.         [theSavePanel setPrompt: DestPrompt];
  1901.         [theSavePanel setTitle: "Convert to"];
  1902.         tempstring = NewCString(strlen(basename) + strlen(DestExtension));
  1903.         sprintf(tempstring, "%s%s", basename, DestExtension);
  1904.         
  1905.         outcome = [theSavePanel runModalForDirectory: directory file: tempstring];
  1906.         
  1907.         FreeCString (tempstring);
  1908.         FreeCString (filename);
  1909.         FreeCString (directory);
  1910.         ////
  1911.         //    Now, check the output of the call to runModalForDirectory:file.  If
  1912.         //    the user canceled, tell the user this.  Otherwise, continue processing.
  1913.         ////
  1914.         if (outcome != 1)
  1915. d880 5
  1916. a884 4
  1917.             [self StoreErrorCode:  ERR_OK
  1918.                 AndText: "The user chose to abort the conversion at this point"];
  1919.             [SourceTitle    setStringValue: "Canceled:"];
  1920.             [Status    setStringValue:  "Conversion process canceled at your request!"];
  1921. d889 2
  1922. a890 3
  1923.             //    Open the destination file.  If we can't, display why, otherwise show the
  1924.             //    user what the destination file name and path is.   Finally, CONVERT
  1925.             //    the bloody file!
  1926. d892 3
  1927. d896 1
  1928. a896 2
  1929.             //    See if the file ends with the proper extension.  If so, copy it into a
  1930.             //    temporary string.  Otherwise, tack the extension onto it...
  1931. d898 3
  1932. a900 13
  1933.             destname = [theSavePanel filename];
  1934.             extenLen = strlen(DestExtension);
  1935.             destLen = strlen(destname);
  1936.             if (strcmp(&destname[destLen - extenLen], DestExtension) == 0)
  1937.             {
  1938.                 tempstring = NewCString(destLen);
  1939.                 strcpy(tempstring, destname);
  1940.             }
  1941.             else
  1942.             {
  1943.                 tempstring = NewCString(destLen+extenLen);
  1944.                 sprintf(tempstring, "%s%s", destname, DestExtension);
  1945.             }
  1946. d902 1
  1947. a902 1
  1948.             //    Make sure that this ne name doesn't refer to the source file
  1949. d904 16
  1950. a919 1
  1951.             if ([sourceFile      SameFileAs:  tempstring] == YES) 
  1952. d921 4
  1953. a924 6
  1954.                 [SourceTitle    setStringValue: "Error:"];
  1955.                 [Status    setStringValue:  "These refer to the same file.  This is not allowed!" ];
  1956.  
  1957.                 [self StoreErrorCode:  ERR_TRIEDTOOVERWRITE
  1958.                     AndText: "Attempt to overwrite source file with destination file! Conversion NOT done."];
  1959.                 [sourceFile    Close];
  1960. d928 13
  1961. a940 3
  1962.                 destFile = [self openDestFile: tempstring];
  1963.                 [destFile CreateAndOpenFor:  FILE_WRITE];
  1964.                 if ([destFile GetErrorCode] != ERR_OK)
  1965. d942 18
  1966. a959 2
  1967.                     [SourceTitle    setStringValue: "Failed:"];
  1968.                     [Status    setStringValue: [destFile   GetErrorText]];
  1969. d961 1
  1970. d965 23
  1971. a987 6
  1972.                     free(tempstring);    // not needed anymore.
  1973.                     //
  1974.                     //    we opened the dest file.  So, tell the user, and continue.
  1975.                     //
  1976.                     filename = [destFile GetFilename];
  1977.                     directory = [destFile GetDirectory];
  1978. d989 12
  1979. a1000 2
  1980.                     [DestFileName    setStringValue: filename];
  1981.                     [DestPath    setStringValue: directory];
  1982. d1002 6
  1983. a1007 12
  1984.                     [Status    setStringValue:  "Converting"];
  1985.                     
  1986.                     [self ConvertFrom: sourceFile To: destFile];
  1987.                     
  1988.                     FreeCString (filename);
  1989.                     FreeCString (tempstring);
  1990.                     FreeCString (directory);
  1991.                     //
  1992.                     //    Convert the file and then clean up.
  1993.                     //
  1994.                     [destFile        CloseAndSave];
  1995.                     [sourceFile    Close];
  1996. d1029 5
  1997. @
  1998.